﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PlanProd.Contracts;
using PlanProd.Data;
using PlanProd.Contracts.enums;
using PlanProd.Contracts.Misc;
using System.Transactions;
using Web.Framework;
using Web.Framework.Exceptions;
using PlanProd.Contracts.Validation;
using System.Linq.Expressions;
using PlanProd.Contracts.Search;

namespace PlanProd.Services
{
    public class PlanProdAdminService : IPlanProdAdmin
    {
        /// <summary>
        /// Devuelve los productos que no existen
        /// </summary>
        /// <param name="productosAValidar"></param>
        /// <returns></returns>
        public List<ProductoValidation> ValidarProductos(List<ProductoValidation> productosAValidar)
        {
            List<ProductoValidation> productosExistentes;
            List<ProductoValidation> productosARetornar = new List<ProductoValidation>();
            using (PlanProdEntities ctx = new PlanProdEntities())
            {
                IQueryable<DbProducto> query = ctx.DbProductoes;

                Expression<Func<DbProducto, bool>> expression = p => false;

                foreach (ProductoValidation productoAValidar in productosAValidar)
                {
                    if (!String.IsNullOrEmpty(productoAValidar.ColorId) && !String.IsNullOrEmpty(productoAValidar.Producto))
                    {
                        expression = expression.Or(p => p.ProductoIdExterno == productoAValidar.Producto && p.ColorIdExterno == productoAValidar.ColorId);
                    }
                }

                query.Where(expression);

                productosExistentes = (from p in query
                                       select new ProductoValidation()
                                       {
                                           Producto = p.ProductoIdExterno,
                                           ColorId = p.ColorIdExterno
                                       }).ToList();

                foreach (ProductoValidation productoAValidar in productosAValidar)
                {
                    if (!String.IsNullOrEmpty(productoAValidar.ColorId) && !String.IsNullOrEmpty(productoAValidar.Producto))
                    {
                        if (!(productosExistentes.Where(p => p.Producto == productoAValidar.Producto && p.ColorId == productoAValidar.ColorId).Count() > 0))
                        {
                            productosARetornar.Add(productoAValidar);
                        }
                    }
                }
            }

            return productosARetornar;
        }

        public List<Temporada> GetTemporadas(ServiceOptions serviceOptions, string nombre, int empresaId, DateTime? desde, DateTime? hasta, bool? activa)
        {
            using (PlanProdEntities ctx = new PlanProdEntities())
            {
                IQueryable<DbTemporada> query = ctx.DbTemporadas;
                query = query.Where(t => t.EmpresaId == empresaId);

                if (!String.IsNullOrEmpty(nombre))
                {
                    nombre = nombre.ToLower();
                    query = query.Where(t => t.Nombre.Contains(nombre));
                }

                if (activa.HasValue)
                {
                    query = query.Where(t => t.Activa == activa);
                }

                if (desde.HasValue)
                {
                    query = query.Where(t => t.FechaInicio >= desde.Value);
                }

                if (hasta.HasValue)
                {
                    query = query.Where(t => t.FechaFin < hasta.Value);
                }

                if (serviceOptions != null && serviceOptions.UsePages)
                {
                    query = query.Skip(serviceOptions.GetSkip()).Take(serviceOptions.PageSize);
                }

                return (from t in query
                        select new Temporada()
                        {
                            Id = t.Id,
                            EmpresaId = t.EmpresaId,
                            Nombre = t.Nombre,
                            FechaDesde = t.FechaInicio,
                            FechaHasta = t.FechaFin,
                            NumeroSemanas = t.NumeroSemanas,
                            Activa = t.Activa,
                            LetraTemporada = t.LetraTemporada,
                            AuditFechaCreacion = t.AuditFechaCreacion,
                            AuditFechaModificacion = t.AuditFechaModificacion,
                            AuditUserNameCreacion = t.AuditUserNameCreacion,
                            AuditUserNameModificacion = t.AuditUserNameModificacion
                        }).ToList();
            }
        }

        public Temporada GetTemporadaById(ServiceOptions serviceOptions, int temporadaId)
        {
            using (PlanProdEntities ctx = new PlanProdEntities())
            {
                return (from t in ctx.DbTemporadas
                        where t.Id == temporadaId
                        select new Temporada()
                        {
                            Id = t.Id,
                            EmpresaId = t.EmpresaId,
                            Nombre = t.Nombre,
                            FechaDesde = t.FechaInicio,
                            FechaHasta = t.FechaFin,
                            NumeroSemanas = t.NumeroSemanas,
                            Activa = t.Activa,
                            LetraTemporada = t.LetraTemporada,
                            AuditFechaCreacion = t.AuditFechaCreacion,
                            AuditFechaModificacion = t.AuditFechaModificacion,
                            AuditUserNameCreacion = t.AuditUserNameCreacion,
                            AuditUserNameModificacion = t.AuditUserNameModificacion
                        }).Single();
            }
        }

        public void EliminarTemporada(ServiceOptions serviceOptions, int temporadaId)
        {
            using (TransactionScope tScope = new TransactionScope())
            {
                using (PlanProdEntities ctx = new PlanProdEntities())
                {
                    var temporada = ctx.DbTemporadas.Where(t => t.Id == temporadaId).Single();
                    ctx.DbTemporadas.DeleteObject(temporada);
                    ctx.SaveChanges();
                }

                tScope.Complete();
            }
        }

        public Temporada SaveTemporada(ServiceOptions serviceOptions, Temporada temporada)
        {
            using (TransactionScope tScope = new TransactionScope())
            {
                using (PlanProdEntities ctx = new PlanProdEntities())
                {
                    DbTemporada dbTemporada;

                    dbTemporada = ctx.DbTemporadas.Where(t => t.Id == temporada.Id).SingleOrDefault();
                    if (dbTemporada == null)
                    {
                        dbTemporada = new DbTemporada();
                        ctx.DbTemporadas.AddObject(dbTemporada);
                        dbTemporada.AuditUserNameCreacion = serviceOptions.UserName;
                        dbTemporada.AuditFechaCreacion = DateTime.Now;
                    }
                    else
                    {
                        dbTemporada.AuditUserNameModificacion = serviceOptions.UserName;
                        dbTemporada.AuditFechaModificacion = DateTime.Now;
                    }

                    if (temporada.FechaDesde > temporada.FechaHasta)
                        throw new Exception("La fecha desde debe ser menor a la fecha hasta");

                    dbTemporada.Activa = temporada.Activa;
                    dbTemporada.EmpresaId = temporada.EmpresaId;
                    dbTemporada.Nombre = temporada.Nombre;
                    dbTemporada.FechaInicio = temporada.FechaDesde;
                    dbTemporada.FechaFin = temporada.FechaHasta;
                    temporada.NumeroSemanas = Temporada.GetCantidadSemanas(temporada.FechaDesde, temporada.FechaHasta);
                    dbTemporada.NumeroSemanas = temporada.NumeroSemanas;
                    dbTemporada.LetraTemporada = temporada.LetraTemporada;

                    ctx.SaveChanges();
                    temporada.Id = dbTemporada.Id;
                }

                tScope.Complete();

                return temporada;
            }
        }

        public List<CausaDesvio> GetCausasDesvios(ServiceOptions serviceOptions, int empresaId, string nombreLike)
        {
            using (PlanProdEntities ctx = new PlanProdEntities())
            {
                IQueryable<DbDesvioCausa> query = ctx.DbDesvioCausas.Where(d => d.EmpresaId == empresaId);

                if (!String.IsNullOrEmpty(nombreLike))
                {
                    query = query.Where(d => d.Nombre.Contains(nombreLike));
                }

                if (serviceOptions != null && serviceOptions.UsePages)
                {
                    query = query.Skip(serviceOptions.GetSkip()).Take(serviceOptions.PageSize);
                }

                return (from d in query
                        select new CausaDesvio()
                        {
                            Id = d.Id,
                            Nombre = d.Nombre,
                            AuditFechaCreacion = d.AuditFechaCreacion,
                            AuditFechaModificacion = d.AuditFechaModificacion,
                            AuditUserNameCreacion = d.AuditUserNameCreacion,
                            AuditUserNameModificacion = d.AuditUserNameModificacion

                        }).ToList();
            }
        }

        public CausaDesvio GetCausaDeDesvioById(ServiceOptions serviceOptions, int causaDesvioId)
        {
            using (PlanProdEntities ctx = new PlanProdEntities())
            {
                IQueryable<DbDesvioCausa> query = ctx.DbDesvioCausas.Where(d => d.Id == causaDesvioId);

                return (from d in query
                        select new CausaDesvio()
                        {
                            Id = d.Id,
                            Nombre = d.Nombre,
                            EmpresaId = d.EmpresaId,
                            AuditFechaCreacion = d.AuditFechaCreacion,
                            AuditFechaModificacion = d.AuditFechaModificacion,
                            AuditUserNameCreacion = d.AuditUserNameCreacion,
                            AuditUserNameModificacion = d.AuditUserNameModificacion
                        }).Single();
            }
        }

        public CausaDesvio SaveCausaDesvio(ServiceOptions serviceOptions, CausaDesvio causaDesvio)
        {
            using (TransactionScope tScope = new TransactionScope())
            {
                using (PlanProdEntities ctx = new PlanProdEntities())
                {
                    DbDesvioCausa dbDesvioCausa;

                    dbDesvioCausa = ctx.DbDesvioCausas.Where(t => t.Id == causaDesvio.Id).SingleOrDefault();
                    if (dbDesvioCausa == null)
                    {
                        dbDesvioCausa = new DbDesvioCausa();
                        ctx.DbDesvioCausas.AddObject(dbDesvioCausa);
                        dbDesvioCausa.AuditFechaCreacion = DateTime.Now;
                        dbDesvioCausa.AuditUserNameCreacion = serviceOptions.UserName;
                    }
                    else
                    {
                        dbDesvioCausa.AuditFechaModificacion = DateTime.Now;
                        dbDesvioCausa.AuditUserNameModificacion = serviceOptions.UserName;
                    }

                    dbDesvioCausa.EmpresaId = causaDesvio.EmpresaId;
                    dbDesvioCausa.Nombre = causaDesvio.Nombre;

                    ctx.SaveChanges();

                    causaDesvio.Id = dbDesvioCausa.Id;

                    tScope.Complete();

                    return causaDesvio;
                }
            }
        }

        public void EliminarDesvioCausa(ServiceOptions serviceOptions, int causaDesvioId)
        {
            using (TransactionScope tScope = new TransactionScope())
            {
                using (PlanProdEntities ctx = new PlanProdEntities())
                {
                    var desvioCausa = ctx.DbDesvioCausas.Where(t => t.Id == causaDesvioId).Single();
                    ctx.DbDesvioCausas.DeleteObject(desvioCausa);
                    ctx.SaveChanges();
                }

                tScope.Complete();
            }
        }

        public Desvio GetDesvioById(ServiceOptions serviceOptions, int desvioId)
        {
            Desvio desvio;

            using (PlanProdEntities ctx = new PlanProdEntities())
            {
                DbPlanProdDesvio dbDesvio = ctx.DbPlanProdDesvios.Where(d => d.Id == desvioId).Single();

                desvio = GetDesvioFromDbDesvio(dbDesvio);
            }

            return desvio;
        }

        private Desvio GetDesvioFromDbDesvio(DbPlanProdDesvio dbDesvio)
        {
            Desvio desvio = new Desvio();

            desvio.Id = dbDesvio.Id;
            desvio.PlanProduccionDetallePrimeraId = dbDesvio.PlanProduccionDetalleId;
            desvio.PlanProduccionDetalleSegundaId = dbDesvio.PlanProduccionDetalleSegundaId;
            desvio.Estado = dbDesvio.Estado;
            desvio.UsuarioAprobador = dbDesvio.UsuarioAprobacion;
            desvio.FechaAprobacion = dbDesvio.FechaAprobacion;
            desvio.CausaDesvioId = dbDesvio.DesvioCausaId;
            desvio.CausaDesvioNombre = dbDesvio.DesvioCausa.Nombre;
            desvio.UsuarioCreador = dbDesvio.UsuarioCreacion;
            desvio.FechaCreacion = dbDesvio.FechaCreacion;
            desvio.ProductoId = dbDesvio.PlanProduccionDetalle.ProductoId;
            desvio.ProductoSapArticuloNombre = dbDesvio.PlanProduccionDetalle.Producto.ProductoNombre;
            desvio.ProductoSapColorNombre = dbDesvio.PlanProduccionDetalle.Producto.ColorNombreExterno;
            desvio.PlanProduccionId = dbDesvio.PlanProduccionDetalle.PlanProduccion.Id;

            desvio.AuditFechaCreacion = dbDesvio.AuditFechaCreacion;
            desvio.AuditFechaModificacion = dbDesvio.AuditFechaModificacion;
            desvio.AuditUserNameCreacion = dbDesvio.AuditUserNameCreacion;
            desvio.AuditUserNameModificacion = dbDesvio.AuditUserNameModificacion;

            foreach (DbPlanProdDesvioDetalle dbDesvioDetalle in dbDesvio.PlanProdDesvioDetalles)
            {
                DesvioDetalle desvioDetalle = new DesvioDetalle();
                desvioDetalle.DesvioId = desvio.Id;
                desvioDetalle.Id = dbDesvioDetalle.Id;
                desvioDetalle.DetalleProductoCantidadId = dbDesvioDetalle.DetalleCantidadesId;
                desvioDetalle.CantidadInicial = dbDesvioDetalle.CantidadOriginal;
                desvioDetalle.CantidadFinal = dbDesvioDetalle.CantidadFinal;
                desvioDetalle.NumeroSemana = dbDesvioDetalle.PlanProduccionDetalleCantidade.NumeroSemana;

                desvioDetalle.AuditFechaCreacion = dbDesvioDetalle.AuditFechaCreacion;
                desvioDetalle.AuditFechaModificacion = dbDesvioDetalle.AuditFechaModificacion;
                desvioDetalle.AuditUserNameCreacion = dbDesvioDetalle.AuditUserNameCreacion;
                desvioDetalle.AuditUserNameModificacion = dbDesvioDetalle.AuditUserNameModificacion;

                desvio.DesvioDetalles.Add(desvioDetalle);
            }

            return desvio;
        }

        public Desvio GetDesvioByProductoDelPlanDeProduccion(ServiceOptions serviceOptions, int detalleProductoId)
        {
            Desvio desvio = null;
            string estadoAprobadoEnProgreso = Convert.ToString((char)EstadoCharAprobado.EnProgreso);
            string estadoAprobadoEsperandoAprobacion = Convert.ToString((char)EstadoCharAprobado.EsperandoAprobacion);

            using (TransactionScope tScope = new TransactionScope())
            {
                using (PlanProdEntities ctx = new PlanProdEntities())
                {
                    DbPlanProdDesvio dbDesvio = ctx.DbPlanProdDesvios.Where(d => d.PlanProduccionDetalleId == detalleProductoId && (d.Estado == estadoAprobadoEnProgreso || d.Estado == estadoAprobadoEsperandoAprobacion)).SingleOrDefault();

                    var producto = (from prod in ctx.DbProductoes
                                    join detalle in ctx.DbPlanProduccionDetalles on prod.Id equals detalle.ProductoId
                                    where detalle.Id == detalleProductoId
                                    select prod).Single();

                    bool esProductoDePrimera = producto.CalidadId == 1;

                    if (dbDesvio == null)
                    {
                        // Se puede dar que el parametro sea de un desvio para un producto de segunda
                        dbDesvio = ctx.DbPlanProdDesvios.Where(d => d.PlanProduccionDetalleSegundaId.HasValue && d.PlanProduccionDetalleSegundaId.Value == detalleProductoId && (d.Estado == estadoAprobadoEnProgreso || d.Estado == estadoAprobadoEsperandoAprobacion)).SingleOrDefault();
                    }

                    if (dbDesvio != null)
                    {
                        desvio = GetDesvioFromDbDesvio(dbDesvio);
                    }
                    else
                    {
                        // No existe desvio para primera ni para segunda.

                        // Hack, tengo que obtener correctamente el parametro de si el detalle de producto es para un producto de primera o segunda
                        // y devolverselo a la UI, se lo devuelvo en el objeto desvio, aunque este no represente nada. Es la relación entre
                        // los productos lo que devuelvo
                        // La otra opcion es grabar un desvio aqui
                        desvio = new Desvio();
                        desvio.Estado = Convert.ToString((char)EstadoCharAprobado.EnProgreso);

                        DbPlanProduccionDetalle dbPlanProduccionDetalle = ctx.DbPlanProduccionDetalles.Where(d => d.Id == detalleProductoId).Single();

                        int detalleProductoPrimeraId = 0;
                        int? detalleProductoSegundaId = null;
                        DbPlanProduccionDetalle dbPlanProduccionDetalleSegunda = null;

                        if (esProductoDePrimera)
                        {
                            detalleProductoPrimeraId = dbPlanProduccionDetalle.Id;

                            // El producto del detalle tiene segunda?
                            if (dbPlanProduccionDetalle.Producto.ProductoSegundaId.HasValue)
                            {
                                // Obtengo el detalle de segunda si existe
                                dbPlanProduccionDetalleSegunda = ctx.DbPlanProduccionDetalles.Where(d => d.PlanProduccionId == dbPlanProduccionDetalle.PlanProduccionId && d.ProductoId == dbPlanProduccionDetalle.Producto.ProductoSegundaId.Value).SingleOrDefault();
                                if (dbPlanProduccionDetalleSegunda != null)
                                {
                                    // Hay un detalle con el producto de segunda
                                    detalleProductoSegundaId = dbPlanProduccionDetalleSegunda.Id;
                                }
                                else
                                {
                                    // No hay un detalle pero el producto existe, por lo tanto
                                    // creo el detalle de segunda para el plan de producción
                                    dbPlanProduccionDetalleSegunda = new DbPlanProduccionDetalle();
                                    dbPlanProduccionDetalleSegunda.ProductoId = dbPlanProduccionDetalle.Producto.ProductoSegundaId.Value;
                                    dbPlanProduccionDetalleSegunda.PlanProduccionId = dbPlanProduccionDetalle.PlanProduccionId;
                                    dbPlanProduccionDetalleSegunda.AuditFechaCreacion = DateTime.Now;
                                    dbPlanProduccionDetalleSegunda.AuditUserNameCreacion = serviceOptions.UserName;

                                    ctx.DbPlanProduccionDetalles.AddObject(dbPlanProduccionDetalleSegunda);

                                    int numeroSemanas = dbPlanProduccionDetalle.PlanProduccion.Temporada.NumeroSemanas;
                                    for (int i = 1; i <= numeroSemanas; i++)
                                    {
                                        DbPlanProduccionDetalleCantidade dbPlanProduccionDetalleCantidade = new DbPlanProduccionDetalleCantidade();
                                        dbPlanProduccionDetalleCantidade.NumeroSemana = i;
                                        dbPlanProduccionDetalleCantidade.Cantidad = 0;
                                        dbPlanProduccionDetalleSegunda.PlanProduccionDetalleCantidades.Add(dbPlanProduccionDetalleCantidade);
                                        dbPlanProduccionDetalleCantidade.AuditFechaCreacion = DateTime.Now;
                                        dbPlanProduccionDetalleCantidade.AuditUserNameCreacion = serviceOptions.UserName;
                                    }

                                    ctx.SaveChanges();
                                    detalleProductoSegundaId = dbPlanProduccionDetalleSegunda.Id;
                                }
                            }
                        }
                        else
                        {
                            // El producto del detalle no tiene segunda, o no se cargó todavía la segunda o él mismo es de segunda

                            //// Me fijo que ningún producto lo tenga a él de segunda
                            //DbProducto productoQueTieneParamDeSegunda = ctx.DbProductoes.Where(p => p.ProductoSegundaId.HasValue && p.ProductoSegundaId.Value == dbPlanProduccionDetalle.Producto.Id).SingleOrDefault();

                            //// No hay productos que lo tengan a él mismo de segunda
                            //if (productoQueTieneParamDeSegunda == null)
                            //{
                            //    detalleProductoPrimeraId = detalleProductoId;
                            //    detalleProductoSegundaId = null;
                            //}
                            //else
                            //{
                            //    // El producto es de segunda y no tiene cargada la primera?
                            //    throw new Exception("Todavia no se pueden cargar productos solo de segunda");
                            //    // Hay un producto que lo tiene de segunda, entonces el parámetro es de segunda y el que encontré es el de primera
                            //    //detalleProductoPrimeraId = ctx.DbPlanProduccionDetalles.Where(d => d.ProductoId == productoQueTieneParamDeSegunda.Id && d.PlanProduccionId == dbPlanProduccionDetalle.PlanProduccionId).Single().Id;
                            //    //detalleProductoSegundaId = dbPlanProduccionDetalle.Id;
                            //}


                            // El producto del detalle es de segunda

                            // si es de segunda, solo se levanta el de segunda, pero lo mando como primera, sin segunda. Ya que carga como si fuese uno de primera
                            detalleProductoPrimeraId = detalleProductoId;
                            detalleProductoSegundaId = null;
                        }

                        desvio.PlanProduccionDetallePrimeraId = detalleProductoPrimeraId;
                        desvio.PlanProduccionDetalleSegundaId = detalleProductoSegundaId;
                    }
                }

                tScope.Complete();
            }

            return desvio;
        }

        public List<Desvio> GetDesvios(ServiceOptions serviceOptions, int? id, int? planProduccionId, string producto, string color, TipoDesvio? tipoDesvio, EstadoCharAprobado? estadoAprobado, int? causaDesvio, int? destinoId, int? temporadaId, int? proveedorId, DateTime? fechaDesde, DateTime? fechaHasta)
        {
            List<Desvio> desvios = new List<Desvio>();
            using (PlanProdEntities ctx = new PlanProdEntities())
            {
                IQueryable<DbPlanProdDesvio> query = ctx.DbPlanProdDesvios;

                if (id.HasValue)
                {
                    query = query.Where(d => d.Id == id.Value);
                }
                else
                {
                    if (planProduccionId.HasValue)
                    {
                        query = query.Where(d => d.PlanProduccionDetalle.PlanProduccion.Id == planProduccionId.Value);
                    }

                    if (tipoDesvio.HasValue)
                    {
                        int tipoDesvioInt = (int)tipoDesvio.Value;
                        query = query.Where(d => d.TipoMovimiento == tipoDesvioInt);
                    }

                    if (!String.IsNullOrEmpty(producto))
                    {
                        query = query.Where(d => d.PlanProduccionDetalle.Producto.ProductoIdExterno.StartsWith(producto));
                    }

                    if (!String.IsNullOrEmpty(color))
                    {
                        query = query.Where(d => d.PlanProduccionDetalle.Producto.ColorIdExterno == color);
                    }

                    if (estadoAprobado.HasValue)
                    {
                        string estadoAprobadoStr = Convert.ToString((char)estadoAprobado.Value);
                        query = query.Where(d => d.Estado == estadoAprobadoStr);
                    }

                    if (causaDesvio.HasValue)
                    {
                        query = query.Where(d => d.DesvioCausaId == causaDesvio.Value);
                    }

                    if (destinoId.HasValue)
                    {
                        query = query.Where(d => d.PlanProduccionDetalle.PlanProduccion.DestinoId == destinoId.Value);
                    }

                    if (temporadaId.HasValue)
                    {
                        query = query.Where(d => d.PlanProduccionDetalle.PlanProduccion.TemporadaId == temporadaId.Value);
                    }

                    if (proveedorId.HasValue)
                    {
                        query = query.Where(d => d.PlanProduccionDetalle.PlanProduccion.ProveedorId == proveedorId.Value);
                    }

                    if (fechaDesde.HasValue)
                    {
                        query = query.Where(d => d.FechaCreacion >= fechaDesde.Value);
                    }

                    if (fechaHasta.HasValue)
                    {
                        query = query.Where(d => d.FechaCreacion <= fechaHasta.Value);
                    }
                }

                if (serviceOptions != null && serviceOptions.UsePages)
                {
                    query = query.Skip(serviceOptions.GetSkip()).Take(serviceOptions.PageSize);
                }

                var dbDesvios = query.ToList();

                foreach (var dbDesvio in dbDesvios)
                {
                    desvios.Add(GetDesvioFromDbDesvio(dbDesvio));
                }
            }

            return desvios;
        }

        public List<Desvio> SaveDesvioMasivo(ServiceOptions serviceOptions, List<Desvio> desvios)
        {
            using (TransactionScope tScope = new TransactionScope())
            {
                using (PlanProdEntities ctx = new PlanProdEntities())
                {
                    foreach (Desvio desvio in desvios)
                    {
                        SaveDesvio(serviceOptions, desvio);
                    }
                }

                tScope.Complete();
            }

            return desvios;
        }

        public List<Desvio> SaveYAprobarDesvioMasivo(ServiceOptions serviceOptions, List<Desvio> desvios, string userName, EstadoCharAprobado aprobado)
        {
            List<Desvio> desviosGuardados = new List<Desvio>();

            using (TransactionScope tScope = new TransactionScope())
            {
                using (PlanProdEntities ctx = new PlanProdEntities())
                {
                    foreach (Desvio desvio in desvios)
                    {
                        desviosGuardados.Add(SaveDesvio(serviceOptions, desvio));
                        AprobarDesvio(serviceOptions, desvio.Id, userName, aprobado);
                    }
                }

                tScope.Complete();

            }

            return desviosGuardados;
        }

        public Desvio SaveDesvio(ServiceOptions serviceOptions, Desvio desvio)
        {
            using (TransactionScope tScope = new TransactionScope())
            {
                using (PlanProdEntities ctx = new PlanProdEntities())
                {
                    DbPlanProdDesvio dbDesvio = ctx.DbPlanProdDesvios.Where(d => d.Id == desvio.Id).SingleOrDefault();                   

                    if (dbDesvio == null)
                    {
                        dbDesvio = new DbPlanProdDesvio();
                        ctx.DbPlanProdDesvios.AddObject(dbDesvio);                        
                        dbDesvio.AuditFechaCreacion = DateTime.Now;
                        dbDesvio.AuditUserNameCreacion = serviceOptions.UserName;
                        dbDesvio.UsuarioCreacion = desvio.UsuarioCreador;
                        dbDesvio.FechaCreacion = DateTime.Now;
                    }

                    dbDesvio.PlanProduccionDetalleId = desvio.PlanProduccionDetallePrimeraId;
                    dbDesvio.PlanProduccionDetalleSegundaId = desvio.PlanProduccionDetalleSegundaId;
                    dbDesvio.TipoMovimiento = (int)desvio.TipoDesvio;
                    dbDesvio.Estado = desvio.Estado;
                    dbDesvio.DesvioCausaId = desvio.CausaDesvioId;

                    dbDesvio.AuditUserNameModificacion = serviceOptions.UserName;
                    dbDesvio.AuditFechaModificacion = DateTime.Now;


                    // Validar si el tipo de desvio es movimiento que la suma sea igual a la original
                    if (desvio.TipoDesvio == TipoDesvio.Movimiento && !desvio.EsMovimientoValido())
                        throw new Exception("El movimiento debe mantener el total inalterado");

                    foreach (DesvioDetalle desvioDetalle in desvio.DesvioDetalles)
                    {
                        var detalleProductoOriginal = ctx.DbPlanProduccionDetalleCantidades.Where(cant => cant.Id == desvioDetalle.DetalleProductoCantidadId).Single();

                        DbPlanProdDesvioDetalle dbDesvioDetalle = dbDesvio.PlanProdDesvioDetalles.Where(de => de.DetalleCantidadesId == detalleProductoOriginal.Id).SingleOrDefault();

                        if (dbDesvioDetalle == null)
                        {
                            dbDesvioDetalle = new DbPlanProdDesvioDetalle();
                            dbDesvio.PlanProdDesvioDetalles.Add(dbDesvioDetalle);

                            dbDesvioDetalle.AuditFechaCreacion = DateTime.Now;
                            dbDesvioDetalle.AuditUserNameCreacion = serviceOptions.UserName;
                        }

                        dbDesvioDetalle.DetalleCantidadesId = desvioDetalle.DetalleProductoCantidadId;
                        dbDesvioDetalle.CantidadOriginal = detalleProductoOriginal.Cantidad;
                        dbDesvioDetalle.CantidadFinal = desvioDetalle.CantidadFinal;

                        dbDesvioDetalle.AuditFechaModificacion = DateTime.Now;
                        dbDesvioDetalle.AuditUserNameModificacion = serviceOptions.UserName;
                    }

                    ctx.SaveChanges();

                    desvio.Id = dbDesvio.Id;
                }

                tScope.Complete();
            }

            return desvio;
        }

        public void EliminarDesvio(ServiceOptions serviceOptions, int desvioId)
        {
            using (TransactionScope tScope = new TransactionScope())
            {
                using (PlanProdEntities ctx = new PlanProdEntities())
                {
                    DbPlanProdDesvio dbDesvio = ctx.DbPlanProdDesvios.Where(d => d.Id == desvioId).Single();
                    EstadoCharAprobado estado = (EstadoCharAprobado)dbDesvio.Estado[0];
                    if (!(estado == EstadoCharAprobado.Aprobado || estado == EstadoCharAprobado.Rechazado))
                    {
                        List<DbPlanProdDesvioDetalle> detalles = dbDesvio.PlanProdDesvioDetalles.ToList();
                        foreach (var dbDesvioDetalles in detalles)
                        {
                            ctx.DbPlanProdDesvioDetalles.DeleteObject(dbDesvioDetalles);
                        }

                        ctx.DbPlanProdDesvios.DeleteObject(dbDesvio);
                        ctx.SaveChanges();
                    }
                }

                tScope.Complete();
            }
        }

        public void AprobarDesvio(ServiceOptions serviceOptions, int desvioId, string userName, EstadoCharAprobado estado)
        {
            using (TransactionScope tScope = new TransactionScope())
            {
                using (PlanProdEntities ctx = new PlanProdEntities())
                {
                    DbPlanProdDesvio dbPlanProdDesvio = ctx.DbPlanProdDesvios.Where(d => d.Id == desvioId).SingleOrDefault();
                    if (dbPlanProdDesvio != null)
                    {
                        dbPlanProdDesvio.Estado = Convert.ToString((char)estado);

                        if (dbPlanProdDesvio.Estado == Convert.ToString((char)EstadoCharAprobado.Aprobado))
                        {
                            dbPlanProdDesvio.UsuarioAprobacion = userName;
                            dbPlanProdDesvio.FechaAprobacion = DateTime.Now;

                            dbPlanProdDesvio.AuditFechaModificacion = DateTime.Now;
                            dbPlanProdDesvio.AuditUserNameModificacion = serviceOptions.UserName;

                            foreach (DbPlanProdDesvioDetalle dbDesvioDetalle in dbPlanProdDesvio.PlanProdDesvioDetalles)
                            {
                                var detalleProductoOriginal = ctx.DbPlanProduccionDetalleCantidades.Where(cant => cant.Id == dbDesvioDetalle.DetalleCantidadesId).Single();
                                detalleProductoOriginal.Cantidad = dbDesvioDetalle.CantidadFinal;

                                detalleProductoOriginal.AuditUserNameModificacion = serviceOptions.UserName;
                                detalleProductoOriginal.AuditFechaModificacion = DateTime.Now;
                            }

                            DbPlanProduccion planProd = (from desvio in ctx.DbPlanProdDesvios
                                                         where desvio.Id == desvioId
                                                         select desvio.PlanProduccionDetalle.PlanProduccion).Single();

                            planProd.AuditUserNameModificacion = serviceOptions.UserName;
                            planProd.AuditFechaModificacion = DateTime.Now;
                        }

                        ctx.SaveChanges();
                    }
                }

                tScope.Complete();
            }
        }

        public List<Destino> GetDestinos(ServiceOptions serviceOptions, int empresaId, string nombreLike)
        {
            using (PlanProdEntities ctx = new PlanProdEntities())
            {
                IQueryable<DbDestino> query = ctx.DbDestinoes.Where(d => d.EmpresaId == empresaId);

                if (!String.IsNullOrEmpty(nombreLike))
                {
                    query = query.Where(d => d.Nombre.Contains(nombreLike));
                }

                if (serviceOptions != null && serviceOptions.UsePages)
                {
                    query = query.Skip(serviceOptions.GetSkip()).Take(serviceOptions.PageSize);
                }

                return (from d in query
                        select new Destino()
                        {
                            Id = d.Id,
                            Nombre = d.Nombre,
                            EmpresaId = d.EmpresaId,
                            AuditFechaCreacion = d.AuditFechaCreacion,
                            AuditFechaModificacion = d.AuditFechaModificacion,
                            AuditUserNameCreacion = d.AuditUserNameCreacion,
                            AuditUserNameModificacion = d.AuditUserNameModificacion
                        }).ToList();
            }
        }

        public Destino GetDestinoById(ServiceOptions serviceOptions, int destinoId)
        {
            using (PlanProdEntities ctx = new PlanProdEntities())
            {
                IQueryable<DbDestino> query = ctx.DbDestinoes.Where(d => d.Id == destinoId);

                return (from d in query
                        select new Destino()
                        {
                            Id = d.Id,
                            Nombre = d.Nombre,
                            EmpresaId = d.EmpresaId,
                            AuditFechaCreacion = d.AuditFechaCreacion,
                            AuditFechaModificacion = d.AuditFechaModificacion,
                            AuditUserNameCreacion = d.AuditUserNameCreacion,
                            AuditUserNameModificacion = d.AuditUserNameModificacion
                        }).Single();
            }
        }

        public Destino SaveDestino(ServiceOptions serviceOptions, Destino destino)
        {
            using (TransactionScope tScope = new TransactionScope())
            {
                using (PlanProdEntities ctx = new PlanProdEntities())
                {
                    DbDestino dbDestino;

                    dbDestino = ctx.DbDestinoes.Where(t => t.Id == destino.Id).SingleOrDefault();
                    if (dbDestino == null)
                    {
                        dbDestino = new DbDestino();
                        ctx.DbDestinoes.AddObject(dbDestino);
                        dbDestino.AuditFechaCreacion = DateTime.Now;
                        dbDestino.AuditUserNameCreacion = serviceOptions.UserName;
                    }
                    else
                    {
                        dbDestino.AuditFechaModificacion = DateTime.Now;
                        dbDestino.AuditUserNameModificacion = serviceOptions.UserName;
                    }

                    dbDestino.EmpresaId = destino.EmpresaId;
                    dbDestino.Nombre = destino.Nombre;

                    ctx.SaveChanges();
                    destino.Id = dbDestino.Id;
                }

                tScope.Complete();
                return destino;
            }
        }

        public void EliminarDestino(ServiceOptions serviceOptions, int destinoId)
        {
            using (TransactionScope tScope = new TransactionScope())
            {
                using (PlanProdEntities ctx = new PlanProdEntities())
                {
                    var destino = ctx.DbDestinoes.Where(t => t.Id == destinoId).Single();
                    ctx.DbDestinoes.DeleteObject(destino);
                    ctx.SaveChanges();
                }

                tScope.Complete();
            }
        }

        public List<Proveedor> GetProveedores(ServiceOptions serviceOptions, SearchProveedorCriteria criteria)
        {
            using (PlanProdEntities ctx = new PlanProdEntities())
            {
                IQueryable<DbProveedor> query = ctx.DbProveedors;

                if (criteria.Id.HasValue)
                {
                    query = query.Where(p => p.Id == criteria.Id.Value);
                }

                if (!String.IsNullOrEmpty(criteria.RazonSocial))
                {
                    query = query.Where(p => p.RazonSocial.Contains(criteria.RazonSocial));
                }

                query = query.Where(p => p.EmpresaId == criteria.EmpresaId);

                if (serviceOptions != null && serviceOptions.UsePages)
                {
                    query = query.Skip(serviceOptions.GetSkip()).Take(serviceOptions.PageSize);
                }

                return (from p in query
                        orderby p.RazonSocial
                        select new Proveedor()
                        {
                            Id = p.Id,
                            EmpresaId = p.EmpresaId,
                            ProveedorIdExterno = p.ProveedorIdExterno,
                            RazonSocial = p.RazonSocial
                        }).ToList();
            }
        }

        public List<PedidoComercial> GetPedidosComerciales(ServiceOptions serviceOptions, int? pedidoId, int? temporadaId, int? destinoId, DateTime? fechaDesde, DateTime? fechaHasta, string nombreLike)
        {
            using (PlanProdEntities ctx = new PlanProdEntities())
            {
                IQueryable<DbPedidoComercial> query = ctx.DbPedidoComercials;
                if (pedidoId.HasValue)
                {
                    query = query.Where(p => p.Id == pedidoId.Value);
                }
                if (temporadaId.HasValue)
                {
                    query = query.Where(p => p.TemporadaId == temporadaId.Value);
                }
                if (destinoId.HasValue)
                {
                    query = query.Where(p => p.DestinoId == destinoId.Value);
                }
                if (fechaDesde.HasValue)
                {
                    query = query.Where(p => p.AuditFechaModificacion.Value >= fechaDesde.Value);
                }
                if (fechaHasta.HasValue)
                {
                    query = query.Where(p => p.AuditFechaModificacion.Value <= fechaHasta.Value);
                }
                if (!String.IsNullOrEmpty(nombreLike))
                {
                    query = query.Where(p => p.Nombre.Contains(nombreLike));
                }

                if (serviceOptions != null && serviceOptions.UsePages)
                {
                    query = query.Skip(serviceOptions.GetSkip()).Take(serviceOptions.PageSize);
                }

                return (from p in query
                        select new PedidoComercial()
                        {
                            Id = p.Id,
                            DestinoId = p.DestinoId,
                            DestinoNombre = p.Destino.Nombre,
                            TemporadaId = p.TemporadaId,
                            TemporadaNombre = p.Temporada.Nombre,
                            EmpresaId = p.EmpresaId,
                            Nombre = p.Nombre,
                            CantidadPrendas = p.PedidoComercialDetalles.Sum(pd => pd.Cantidad),
                            AuditFechaCreacion = p.AuditFechaCreacion,
                            AuditFechaModificacion = p.AuditFechaModificacion,
                            AuditUserNameCreacion = p.AuditUserNameCreacion,
                            AuditUserNameModificacion = p.AuditUserNameModificacion
                        }).ToList();
            }
        }

        public PedidoComercial GetPedidoComercialById(ServiceOptions serviceOptions, int id)
        {
            using (PlanProdEntities ctx = new PlanProdEntities())
            {
                PedidoComercial pedidoComecial = (from p in ctx.DbPedidoComercials
                                                  where p.Id == id
                                                  select new PedidoComercial()
                                                  {
                                                      Id = p.Id,
                                                      DestinoId = p.DestinoId,
                                                      DestinoNombre = p.Destino.Nombre,
                                                      TemporadaId = p.TemporadaId,
                                                      TemporadaNombre = p.Temporada.Nombre,
                                                      EmpresaId = p.EmpresaId,
                                                      CantidadPrendas = p.PedidoComercialDetalles.Sum(pd => pd.Cantidad),
                                                      Nombre = p.Nombre,
                                                      AuditFechaCreacion = p.AuditFechaCreacion,
                                                      AuditFechaModificacion = p.AuditFechaModificacion,
                                                      AuditUserNameCreacion = p.AuditUserNameCreacion,
                                                      AuditUserNameModificacion = p.AuditUserNameModificacion
                                                  }).Single();

                var detalles = from d in ctx.DbPedidoComercialDetalles
                               where d.PedidoComercialId == pedidoComecial.Id
                               select new PedidoComercialDetalle()
                               {
                                   Id = d.Id,
                                   PedidoComercialId = d.PedidoComercialId,
                                   ProductoId = d.ProductoId,
                                   ProductoIdExterno = d.Producto.ProductoIdExterno,
                                   ProductoNombre = d.Producto.ProductoNombre,
                                   ColorId = d.Producto.ColorIdExterno,
                                   ColorNombre = d.Producto.ColorNombreExterno,
                                   Cantidad = d.Cantidad,
                                   AuditFechaCreacion = d.AuditFechaCreacion,
                                   AuditFechaModificacion = d.AuditFechaModificacion,
                                   AuditUserNameCreacion = d.AuditUserNameCreacion,
                                   AuditUserNameModificacion = d.AuditUserNameModificacion
                               };

                pedidoComecial.Detalle.AddRange(detalles.ToList());

                return pedidoComecial;
            }
        }

        public void EliminarPedidoComercial(ServiceOptions serviceOptions, int pedidoComercialId)
        {
            using (TransactionScope tScope = new TransactionScope())
            {
                using (PlanProdEntities ctx = new PlanProdEntities())
                {
                    var pedidoComercial = ctx.DbPedidoComercials.Where(p => p.Id == pedidoComercialId).Single();
                    pedidoComercial.PedidoComercialDetalles.Clear();
                    ctx.DbPedidoComercials.DeleteObject(pedidoComercial);
                    ctx.SaveChanges();
                }

                tScope.Complete();
            }
        }

        public void EliminarPedidoComercialDetalleAll(ServiceOptions serviceOptions, int pedidoComercialId)
        {
            using (TransactionScope tScope = new TransactionScope())
            {
                using (PlanProdEntities ctx = new PlanProdEntities())
                {
                    var pedidoComercial = ctx.DbPedidoComercials.Where(p => p.Id == pedidoComercialId).Single();
                    var pedidoComercialDetalleList = pedidoComercial.PedidoComercialDetalles.ToList();
                    pedidoComercial.PedidoComercialDetalles.Clear();
                    foreach (var detalle in pedidoComercialDetalleList)
                    {
                        ctx.DbPedidoComercialDetalles.DeleteObject(detalle);
                    }

                    ctx.SaveChanges();
                }

                tScope.Complete();
            }
        }

        public PedidoComercial SavePedidoComercial(ServiceOptions serviceOptions, PedidoComercial pedidoComercial)
        {
            using (TransactionScope tScope = new TransactionScope())
            {
                using (PlanProdEntities ctx = new PlanProdEntities())
                {
                    // Debería hacer un update, pero en este caso solo se usa para crear pedidos
                    DbPedidoComercial dbPedidoComercial = ctx.DbPedidoComercials.Where(p => p.Id == pedidoComercial.Id).SingleOrDefault();

                    if (dbPedidoComercial == null)
                    {
                        dbPedidoComercial = new DbPedidoComercial();
                        ctx.DbPedidoComercials.AddObject(dbPedidoComercial);
                        dbPedidoComercial.AuditUserNameCreacion = serviceOptions.UserName;
                        dbPedidoComercial.AuditFechaCreacion = DateTime.Now;
                        dbPedidoComercial.AuditUserNameModificacion = serviceOptions.UserName;
                        dbPedidoComercial.AuditFechaModificacion = DateTime.Now;
                    }
                    else
                    {
                        dbPedidoComercial.AuditUserNameModificacion = serviceOptions.UserName;
                        dbPedidoComercial.AuditFechaModificacion = DateTime.Now;
                    }

                    dbPedidoComercial.Nombre = pedidoComercial.Nombre;
                    dbPedidoComercial.TemporadaId = pedidoComercial.TemporadaId;
                    dbPedidoComercial.DestinoId = pedidoComercial.DestinoId;
                    dbPedidoComercial.EmpresaId = pedidoComercial.EmpresaId;

                    List<DbPedidoComercialDetalle> dbNewPedidoComercialDetalleList = new List<DbPedidoComercialDetalle>();
                    foreach (PedidoComercialDetalle pedidoComercialDetalle in pedidoComercial.Detalle)
                    {
                        DbPedidoComercialDetalle dbPedidoComercialDetalle = dbPedidoComercial.PedidoComercialDetalles.Where(d => d.Producto.ColorIdExterno == pedidoComercialDetalle.ColorId && d.Producto.ProductoIdExterno == pedidoComercialDetalle.ProductoIdExterno).SingleOrDefault();
                        if (dbPedidoComercialDetalle == null)
                        {
                            dbPedidoComercialDetalle = new DbPedidoComercialDetalle();
                            dbPedidoComercialDetalle.ProductoId = GetProductoIdFromProductoColor(ctx, pedidoComercialDetalle.ProductoIdExterno, pedidoComercialDetalle.ColorId);
                            dbPedidoComercialDetalle.Cantidad = pedidoComercialDetalle.Cantidad;
                            dbPedidoComercialDetalle.AuditUserNameCreacion = serviceOptions.UserName;
                            dbPedidoComercialDetalle.AuditFechaCreacion = DateTime.Now;
                            dbNewPedidoComercialDetalleList.Add(dbPedidoComercialDetalle);
                        }
                        else
                        {
                            dbPedidoComercialDetalle.Cantidad += pedidoComercialDetalle.Cantidad;

                            dbPedidoComercialDetalle.AuditUserNameModificacion = serviceOptions.UserName;
                            dbPedidoComercialDetalle.AuditFechaModificacion = DateTime.Now;
                        }
                    }

                    foreach (var dbNewDetalle in dbNewPedidoComercialDetalleList)
                    {
                        dbPedidoComercial.PedidoComercialDetalles.Add(dbNewDetalle);
                    }

                    ctx.SaveChanges();

                    pedidoComercial.Id = dbPedidoComercial.Id;
                    tScope.Complete();

                }
            }

            return pedidoComercial;
        }

        public void UpdatePedidoComercialDetalle(ServiceOptions serviceOptions, List<PedidoComercialDetalle> pedidoComercialDetalleList)
        {
            using (TransactionScope tScope = new TransactionScope())
            {
                using (PlanProdEntities ctx = new PlanProdEntities())
                {
                    if (pedidoComercialDetalleList.Count() > 0)
                    {
                        int pedidoComercialId = pedidoComercialDetalleList.First().PedidoComercialId;
                        DbPedidoComercial dbPedidoComercial = ctx.DbPedidoComercials.Where(p => p.Id == pedidoComercialId).Single();

                        dbPedidoComercial.AuditUserNameModificacion = serviceOptions.UserName;
                        dbPedidoComercial.AuditFechaModificacion = DateTime.Now;

                        List<DbPedidoComercialDetalle> dbNewPedidoComercialDetalleList = new List<DbPedidoComercialDetalle>();
                        foreach (PedidoComercialDetalle pedidoComercialDetalle in pedidoComercialDetalleList)
                        {
                            DbPedidoComercialDetalle dbPedidoComercialDetalle = dbPedidoComercial.PedidoComercialDetalles.Where(d => d.Producto.ColorIdExterno == pedidoComercialDetalle.ColorId && d.Producto.ProductoIdExterno == pedidoComercialDetalle.ProductoIdExterno).SingleOrDefault();
                            if (dbPedidoComercialDetalle == null)
                            {
                                dbPedidoComercialDetalle = new DbPedidoComercialDetalle();
                                dbPedidoComercialDetalle.ProductoId = GetProductoIdFromProductoColor(ctx, pedidoComercialDetalle.ProductoIdExterno, pedidoComercialDetalle.ColorId);
                                dbPedidoComercialDetalle.Cantidad = pedidoComercialDetalle.Cantidad;
                                dbPedidoComercialDetalle.AuditUserNameCreacion = serviceOptions.UserName;
                                dbPedidoComercialDetalle.AuditFechaCreacion = DateTime.Now;
                                dbNewPedidoComercialDetalleList.Add(dbPedidoComercialDetalle);
                            }
                            else
                            {
                                dbPedidoComercialDetalle.Cantidad += pedidoComercialDetalle.Cantidad;
                                dbPedidoComercialDetalle.AuditUserNameModificacion = serviceOptions.UserName;
                                dbPedidoComercialDetalle.AuditFechaModificacion = DateTime.Now;
                            }
                        }

                        foreach (var dbNewDetalle in dbNewPedidoComercialDetalleList)
                        {
                            dbPedidoComercial.PedidoComercialDetalles.Add(dbNewDetalle);
                        }

                        ctx.SaveChanges();
                    }
                }

                tScope.Complete();
            }
        }

        public void EliminarPedidoComercialDetalle(ServiceOptions serviceOptions, int pedidoComercialDetalleId)
        {
            using (TransactionScope tScope = new TransactionScope())
            {
                using (PlanProdEntities ctx = new PlanProdEntities())
                {
                    var pedidoComercialDetalle = ctx.DbPedidoComercialDetalles.Where(d => d.Id == pedidoComercialDetalleId).Single();
                    ctx.DbPedidoComercialDetalles.DeleteObject(pedidoComercialDetalle);
                    ctx.SaveChanges();
                }

                tScope.Complete();
            }
        }

        public List<PresupuestoProduccion> GetPresupuestoProduccion(ServiceOptions serviceOptions, int? presupuestoId, int? temporadaId, int? destinoId, int? proveedorId, EstadoAprobado? estado, DateTime? fechaDesde, DateTime? fechaHasta)
        {
            using (PlanProdEntities ctx = new PlanProdEntities())
            {
                IQueryable<DbPresupuestoProduccion> query = ctx.DbPresupuestoProduccions;

                if (presupuestoId.HasValue)
                {
                    query = query.Where(p => p.Id == presupuestoId.Value);
                }
                else
                {
                    if (temporadaId.HasValue)
                    {
                        query = query.Where(p => p.TemporadaId == temporadaId.Value);
                    }
                    if (destinoId.HasValue)
                    {
                        query = query.Where(p => p.DestinoId == destinoId.Value);
                    }

                    if (proveedorId.HasValue)
                    {
                        query = query.Where(p => p.ProveedorId == proveedorId.Value);
                    }

                    if (estado.HasValue)
                    {
                        if (estado.Value == EstadoAprobado.NoDefinido)
                        {
                            query = query.Where(p => p.Aprobado == null);
                        }
                        else
                        {
                            bool aprobado = estado.Value == EstadoAprobado.Aprobado;
                            query = query.Where(p => p.Aprobado == aprobado);
                        }
                    }

                    if (fechaDesde.HasValue)
                    {
                        query = query.Where(p => p.AuditFechaModificacion.Value >= fechaDesde.Value);
                    }
                    if (fechaHasta.HasValue)
                    {
                        query = query.Where(p => p.AuditFechaModificacion.Value <= fechaHasta.Value);
                    }
                }

                if (serviceOptions != null && serviceOptions.UsePages)
                {
                    query = query.Skip(serviceOptions.GetSkip()).Take(serviceOptions.PageSize);
                }

                List<PresupuestoProduccion> presupuestosDeProduccion = new List<PresupuestoProduccion>();
                presupuestosDeProduccion.AddRange((from p in query
                                                   select new PresupuestoProduccion()
                                                   {
                                                       Id = p.Id,
                                                       DestinoId = p.DestinoId,
                                                       DestinoNombre = p.Destino.Nombre,
                                                       TemporadaId = p.TemporadaId,
                                                       TemporadaNombre = p.Temporada.Nombre,
                                                       EmpresaId = p.EmpresaId,
                                                       ProveedorId = p.ProveedorId,
                                                       ProveedorIdExterno = p.Proveedor.ProveedorIdExterno,
                                                       ProveedorNombre = p.Proveedor.RazonSocial,
                                                       EstadoBool = p.Aprobado,
                                                       PlanProduccionId = p.PlanProduccionId,
                                                       AuditFechaCreacion = p.AuditFechaCreacion,
                                                       AuditFechaModificacion = p.AuditFechaModificacion,
                                                       AuditUserNameCreacion = p.AuditUserNameCreacion,
                                                       AuditUserNameModificacion = p.AuditUserNameModificacion
                                                   }).ToList());

                presupuestosDeProduccion.ForEach(p => p.Estado = p.EstadoBool.HasValue ? (p.EstadoBool.Value ? EstadoAprobado.Aprobado : EstadoAprobado.NoAprobado) : EstadoAprobado.NoDefinido);

                return presupuestosDeProduccion;
            }
        }

        public void EliminarPresupuestoProduccion(ServiceOptions serviceOptions, int presupuestoProduccionId)
        {
            using (TransactionScope tScope = new TransactionScope())
            {
                using (PlanProdEntities ctx = new PlanProdEntities())
                {
                    var presupuestoProduccion = ctx.DbPresupuestoProduccions.Where(d => d.Id == presupuestoProduccionId).Single();
                    if (!presupuestoProduccion.Aprobado.HasValue || !presupuestoProduccion.Aprobado.Value)
                    {
                        ctx.DbPresupuestoProduccions.DeleteObject(presupuestoProduccion);
                        ctx.SaveChanges();
                    }
                }

                tScope.Complete();
            }
        }

        public void AprobarPresupuestoProduccion(ServiceOptions serviceOptions, int presupuestoProduccionId, int empresaId, string userName, bool aprobado)
        {
            using (TransactionScope tScope = new TransactionScope())
            {
                using (PlanProdEntities ctx = new PlanProdEntities())
                {
                    var presupuestoProduccion = ctx.DbPresupuestoProduccions.Where(d => d.Id == presupuestoProduccionId).Single();
                    if (!presupuestoProduccion.Aprobado.HasValue)
                    {
                        // No debe tener cantidades en cero
                        foreach (var ppDetalles in presupuestoProduccion.PresupuestoProduccionDetalles)
                        {
                            bool unaDistintaACero = false;
                            foreach (var ppCant in ppDetalles.PresupuestoProduccionDetalleCantidades)
                            {
                                if (ppCant.Cantidad != 0)
                                {
                                    unaDistintaACero = true;
                                    break;
                                }
                            }

                            if (!unaDistintaACero)
                            {
                                throw new Exception("Existen productos en el presupuesto sin ninguna cantidad asignada");
                            }
                        }

                        presupuestoProduccion.Aprobado = aprobado;
                        presupuestoProduccion.UsuarioAprobacion = userName;
                        presupuestoProduccion.FechaAprobacion = DateTime.Now;
                        presupuestoProduccion.AuditUserNameModificacion = serviceOptions.UserName;
                        presupuestoProduccion.AuditFechaModificacion = DateTime.Now;

                        if (aprobado)
                        {
                            // Generar Plan de Producción
                            DbPlanProduccion dbPlanDeProduccion = new DbPlanProduccion();
                            ctx.DbPlanProduccions.AddObject(dbPlanDeProduccion);
                            dbPlanDeProduccion.TemporadaId = presupuestoProduccion.TemporadaId;
                            dbPlanDeProduccion.ProveedorId = presupuestoProduccion.ProveedorId;
                            dbPlanDeProduccion.DestinoId = presupuestoProduccion.DestinoId;
                            dbPlanDeProduccion.EmpresaId = presupuestoProduccion.EmpresaId;
                            dbPlanDeProduccion.AuditFechaCreacion = DateTime.Now;
                            dbPlanDeProduccion.AuditUserNameCreacion = serviceOptions.UserName;

                            ctx.SaveChanges();
                            presupuestoProduccion.PlanProduccionId = dbPlanDeProduccion.Id;

                            // Creación de detalles
                            foreach (DbPresupuestoProduccionDetalle dbPresupuestoProduccionDetalle in presupuestoProduccion.PresupuestoProduccionDetalles)
                            {
                                DbPlanProduccionDetalle dbPlanProduccionDetalle = new DbPlanProduccionDetalle();
                                //ctx.DbPlanProduccionDetalles.AddObject(dbPlanProduccionDetalle);
                                dbPlanProduccionDetalle.ProductoId = dbPresupuestoProduccionDetalle.ProductoId;
                                dbPlanProduccionDetalle.AuditFechaCreacion = DateTime.Now;
                                dbPlanProduccionDetalle.AuditUserNameCreacion = serviceOptions.UserName;
                                dbPlanDeProduccion.PlanProduccionDetalles.Add(dbPlanProduccionDetalle);

                                // Cantidades por semana
                                foreach (DbPresupuestoProduccionDetalleCantidade dDbPresupuestoProduccionDetalleCantidade in dbPresupuestoProduccionDetalle.PresupuestoProduccionDetalleCantidades)
                                {
                                    DbPlanProduccionDetalleCantidade dbPlanProduccionDetalleCantidade = new DbPlanProduccionDetalleCantidade();
                                    dbPlanProduccionDetalleCantidade.NumeroSemana = dDbPresupuestoProduccionDetalleCantidade.NumeroSemana;
                                    dbPlanProduccionDetalleCantidade.Cantidad = dDbPresupuestoProduccionDetalleCantidade.Cantidad;
                                    dbPlanProduccionDetalleCantidade.AuditFechaCreacion = DateTime.Now;
                                    dbPlanProduccionDetalleCantidade.AuditUserNameCreacion = serviceOptions.UserName;
                                    dbPlanProduccionDetalle.PlanProduccionDetalleCantidades.Add(dbPlanProduccionDetalleCantidade);
                                }
                            }
                        }

                        ctx.SaveChanges();
                    }
                }

                tScope.Complete();
            }
        }

        public void UpdatePresupuestoProduccionDetalle(ServiceOptions serviceOptions, List<PresupuestoProduccionDetalleProd> presupuestoProduccionDetalles)
        {

            using (TransactionScope tScope = new TransactionScope())
            {
                using (PlanProdEntities ctx = new PlanProdEntities())
                {
                    if (presupuestoProduccionDetalles.Count() > 0)
                    {
                        int presupuestoProduccionId = presupuestoProduccionDetalles.First().PresupuestoProduccionId;
                        DbPresupuestoProduccion dbPresupuestoProduccion = ctx.DbPresupuestoProduccions.Where(p => p.Id == presupuestoProduccionId).Single();

                        if (!dbPresupuestoProduccion.Aprobado.HasValue)
                        {
                            SaveDetallePresupuestoProduccion(serviceOptions, ctx, dbPresupuestoProduccion, presupuestoProduccionDetalles);

                            ctx.SaveChanges();
                        }
                    }
                }

                tScope.Complete();
            }
        }

        private void SaveDetallePresupuestoProduccion(ServiceOptions serviceOptions, PlanProdEntities ctx, DbPresupuestoProduccion dbPresupuestoProduccion, List<PresupuestoProduccionDetalleProd> presupuestoProduccionDetalles)
        {
            // Elimino todo lo que habia
            if (!dbPresupuestoProduccion.Aprobado.HasValue)
            {
                DbPresupuestoProduccionDetalle dbDetalle;
                while (dbPresupuestoProduccion.PresupuestoProduccionDetalles.Count > 0)
                {
                    dbPresupuestoProduccion.AuditUserNameModificacion = serviceOptions.UserName;
                    dbPresupuestoProduccion.AuditFechaModificacion = DateTime.Now;

                    dbDetalle = dbPresupuestoProduccion.PresupuestoProduccionDetalles.First();
                    DbPresupuestoProduccionDetalleCantidade dbDetalleCantidad;
                    while (dbDetalle.PresupuestoProduccionDetalleCantidades.Count > 0)
                    {
                        dbDetalleCantidad = dbDetalle.PresupuestoProduccionDetalleCantidades.First();
                        ctx.DbPresupuestoProduccionDetalleCantidades.DeleteObject(dbDetalleCantidad);
                    }

                    dbDetalle.PresupuestoProduccionDetalleCantidades.Clear();

                    ctx.DbPresupuestoProduccionDetalles.DeleteObject(dbDetalle);
                }

                dbPresupuestoProduccion.PresupuestoProduccionDetalles.Clear();

                ctx.SaveChanges();

                // Cargo lo nuevo
                foreach (PresupuestoProduccionDetalleProd ppProd in presupuestoProduccionDetalles)
                {
                    DbPresupuestoProduccionDetalle dbPPDetalle = new DbPresupuestoProduccionDetalle();
                    dbPPDetalle.AuditFechaCreacion = DateTime.Now;
                    dbPPDetalle.AuditUserNameCreacion = serviceOptions.UserName;
                    // Encontrar el producto

                    dbPPDetalle.ProductoId = GetProductoIdFromProductoColor(ctx, ppProd.ProductoIdExterno, ppProd.ColorId);
                    //ctx.DbPresupuestoProduccionDetalles.AddObject(dbPPDetalle);

                    dbPresupuestoProduccion.PresupuestoProduccionDetalles.Add(dbPPDetalle);

                    foreach (PresupuestoProduccionDetalleProdSemanal ppProdCantSemana in ppProd.CantidadesPorSemana)
                    {
                        DbPresupuestoProduccionDetalleCantidade dbCantidadPorSemana = new DbPresupuestoProduccionDetalleCantidade();
                        dbCantidadPorSemana.NumeroSemana = ppProdCantSemana.NumeroSemana;
                        dbCantidadPorSemana.Cantidad = ppProdCantSemana.Cantidad;
                        dbCantidadPorSemana.AuditFechaCreacion = DateTime.Now;
                        dbCantidadPorSemana.AuditUserNameCreacion = serviceOptions.UserName;
                        dbPPDetalle.PresupuestoProduccionDetalleCantidades.Add(dbCantidadPorSemana);
                    }
                }
            }
        }

        private int GetProductoIdFromProductoColor(PlanProdEntities ctx, string productoIdExterno, string colorIdExterno)
        {
            return (from p in ctx.DbProductoes
                    where p.ProductoIdExterno == productoIdExterno
                    && p.ColorIdExterno == colorIdExterno
                    select p.Id).Single();
        }

        public PresupuestoProduccion SavePresupuestoProduccion(ServiceOptions serviceOptions, PresupuestoProduccion presupuestoProduccion)
        {
            using (TransactionScope tScope = new TransactionScope())
            {
                using (PlanProdEntities ctx = new PlanProdEntities())
                {
                    DbPresupuestoProduccion dbPresupuestoProduccion = ctx.DbPresupuestoProduccions.Where(p => p.Id == presupuestoProduccion.Id).SingleOrDefault();

                    if (dbPresupuestoProduccion == null)
                    {
                        dbPresupuestoProduccion = new DbPresupuestoProduccion();
                        ctx.DbPresupuestoProduccions.AddObject(dbPresupuestoProduccion);
                        dbPresupuestoProduccion.AuditUserNameCreacion = serviceOptions.UserName;
                        dbPresupuestoProduccion.AuditFechaCreacion = DateTime.Now;
                    }
                    else
                    {
                        dbPresupuestoProduccion.AuditUserNameModificacion = serviceOptions.UserName;
                        dbPresupuestoProduccion.AuditFechaModificacion = DateTime.Now;
                    }

                    if (!dbPresupuestoProduccion.Aprobado.HasValue)
                    {
                        dbPresupuestoProduccion.TemporadaId = presupuestoProduccion.TemporadaId;
                        dbPresupuestoProduccion.ProveedorId = presupuestoProduccion.ProveedorId;
                        dbPresupuestoProduccion.DestinoId = presupuestoProduccion.DestinoId;
                        dbPresupuestoProduccion.EmpresaId = presupuestoProduccion.EmpresaId;
                        dbPresupuestoProduccion.Aprobado = null;

                        ctx.SaveChanges();

                        SaveDetallePresupuestoProduccion(serviceOptions, ctx, dbPresupuestoProduccion, presupuestoProduccion.DetalleDeProductos);

                        ctx.SaveChanges();

                        presupuestoProduccion.Id = dbPresupuestoProduccion.Id;
                    }
                }

                tScope.Complete();
                return presupuestoProduccion;
            }
        }

        public PresupuestoProduccion GetPresupuestoProduccionById(ServiceOptions serviceOptions, int id)
        {
            using (PlanProdEntities ctx = new PlanProdEntities())
            {
                DbPresupuestoProduccion dbPresupuestoProduccion = ctx.DbPresupuestoProduccions.Where(p => p.Id == id).Single();

                PresupuestoProduccion presupuestoProduccion = new PresupuestoProduccion()
                                                                {
                                                                    Id = dbPresupuestoProduccion.Id,
                                                                    DestinoId = dbPresupuestoProduccion.DestinoId,
                                                                    DestinoNombre = dbPresupuestoProduccion.Destino.Nombre,
                                                                    TemporadaId = dbPresupuestoProduccion.TemporadaId,
                                                                    TemporadaNombre = dbPresupuestoProduccion.Temporada.Nombre,
                                                                    EmpresaId = dbPresupuestoProduccion.EmpresaId,
                                                                    ProveedorId = dbPresupuestoProduccion.ProveedorId,
                                                                    ProveedorIdExterno = dbPresupuestoProduccion.Proveedor.ProveedorIdExterno,
                                                                    ProveedorNombre = dbPresupuestoProduccion.Proveedor.RazonSocial,
                                                                    EstadoBool = dbPresupuestoProduccion.Aprobado,
                                                                    FechaAprobacion = dbPresupuestoProduccion.FechaAprobacion,
                                                                    UserNameAprobacion = dbPresupuestoProduccion.UsuarioAprobacion,
                                                                    PlanProduccionId = dbPresupuestoProduccion.PlanProduccionId,
                                                                    AuditFechaCreacion = dbPresupuestoProduccion.AuditFechaCreacion,
                                                                    AuditFechaModificacion = dbPresupuestoProduccion.AuditFechaModificacion,
                                                                    AuditUserNameCreacion = dbPresupuestoProduccion.AuditUserNameCreacion,
                                                                    AuditUserNameModificacion = dbPresupuestoProduccion.AuditUserNameModificacion
                                                                };

                presupuestoProduccion.Estado = presupuestoProduccion.EstadoBool.HasValue ? (presupuestoProduccion.EstadoBool.Value ? EstadoAprobado.Aprobado : EstadoAprobado.NoAprobado) : EstadoAprobado.NoDefinido;

                int total = 0;
                PresupuestoProduccionDetalleProd ppDetalle;
                foreach (var dbPPDetalle in dbPresupuestoProduccion.PresupuestoProduccionDetalles)
                {
                    ppDetalle = new PresupuestoProduccionDetalleProd()
                    {
                        Id = dbPPDetalle.Id,
                        PresupuestoProduccionId = dbPPDetalle.PresupuestoProduccionId,
                        ProductoId = dbPPDetalle.ProductoId,
                        ProductoIdExterno = dbPPDetalle.Producto.ProductoIdExterno,
                        ProductoNombre = dbPPDetalle.Producto.ProductoNombre,
                        CalidadId = dbPPDetalle.Producto.CalidadId,
                        CalidadNombre = GetCalidadDesc(dbPPDetalle.Producto.CalidadId),
                        ColorId = dbPPDetalle.Producto.ColorIdExterno,
                        ColorNombre = dbPPDetalle.Producto.ColorNombreExterno
                    };

                    presupuestoProduccion.DetalleDeProductos.Add(ppDetalle);

                    PresupuestoProduccionDetalleProdSemanal ppDetalleCantidad;
                    foreach (var dbPPSemanaCantidad in dbPPDetalle.PresupuestoProduccionDetalleCantidades)
                    {
                        ppDetalleCantidad = new PresupuestoProduccionDetalleProdSemanal()
                        {
                            Id = dbPPSemanaCantidad.Id,
                            NumeroSemana = dbPPSemanaCantidad.NumeroSemana,
                            Cantidad = dbPPSemanaCantidad.Cantidad,
                            AuditFechaCreacion = dbPPSemanaCantidad.AuditFechaCreacion,
                            AuditFechaModificacion = dbPPSemanaCantidad.AuditFechaModificacion,
                            AuditUserNameCreacion = dbPPSemanaCantidad.AuditUserNameCreacion,
                            AuditUserNameModificacion = dbPPSemanaCantidad.AuditUserNameModificacion
                        };

                        total += dbPPSemanaCantidad.Cantidad;

                        ppDetalle.CantidadesPorSemana.Add(ppDetalleCantidad);
                    }
                }

                presupuestoProduccion.TotalPresupuesto = total;

                return presupuestoProduccion;
            }
        }

        public List<Producto> GetProductosForPresupuesto(ServiceOptions serviceOptions, string codigoProducto, string descripcionProducto, int presupuestoProduccionId)
        {
            List<Producto> productoList = new List<Producto>();
            using (PlanProdEntities ctx = new PlanProdEntities())
            {
                IQueryable<DbProducto> query = ctx.DbProductoes;

                if (!String.IsNullOrEmpty(codigoProducto))
                {
                    query = query.Where(p => p.ProductoIdExterno.StartsWith(codigoProducto));
                }

                if (!String.IsNullOrEmpty(descripcionProducto))
                {
                    query = query.Where(p => p.ProductoNombre.Contains(descripcionProducto));
                }

                productoList = (from p in query
                                where !(from detalle in ctx.DbPresupuestoProduccionDetalles
                                        where detalle.PresupuestoProduccionId == presupuestoProduccionId
                                        select detalle.ProductoId).Contains(p.Id)
                                select new Producto()
                                {
                                    Id = p.Id,
                                    ProductoIdExterno = p.ProductoIdExterno,
                                    ProductoNombre = p.ProductoNombre,
                                    CalidadId = p.CalidadId,
                                    ColorIdExterno = p.ColorIdExterno,
                                    ColorNombreExterno = p.ColorNombreExterno,
                                    TemporadaLetra = p.TemporadaLetra
                                }).ToList();

                productoList.ForEach(p => p.Calidad = GetCalidadDesc(p.CalidadId));

            }

            return productoList;
        }

        private static string GetCalidadDesc(int calidadId)
        {
            string calidad = string.Empty;

            if (calidadId == 1)
                calidad = "Primera";
            else if (calidadId == 2)
                calidad = "Segunda";
            else
                calidad = "No especifica";

            return calidad;
        }

        public void UpdatePresupuestoProduccionItems(ServiceOptions serviceOptions, int presupuestoProduccionId, List<UpdateCantidadPlanProd> updates, List<int> deletedProductItems)
        {
            using (TransactionScope tScope = new TransactionScope())
            {
                using (PlanProdEntities ctx = new PlanProdEntities())
                {
                    DbPresupuestoProduccion dbPresupuestoProduccion = ctx.DbPresupuestoProduccions.Where(p => p.Id == presupuestoProduccionId).Single();

                    if (!dbPresupuestoProduccion.Aprobado.HasValue)
                    {

                        foreach (UpdateCantidadPlanProd update in updates)
                        {
                            DbPresupuestoProduccionDetalle dbPPDetalle;

                            dbPPDetalle = dbPresupuestoProduccion.PresupuestoProduccionDetalles.Where(d => d.ProductoId == update.ProductoId).SingleOrDefault();

                            if (dbPPDetalle == null)
                            {
                                dbPPDetalle = new DbPresupuestoProduccionDetalle();
                                dbPPDetalle.AuditUserNameCreacion = serviceOptions.UserName;
                                dbPPDetalle.AuditFechaCreacion = DateTime.Now;
                                dbPresupuestoProduccion.PresupuestoProduccionDetalles.Add(dbPPDetalle);
                            }
                            else
                            {
                                dbPPDetalle = dbPresupuestoProduccion.PresupuestoProduccionDetalles.Where(p => p.Id == update.Id).Single();
                                dbPPDetalle.AuditUserNameModificacion = serviceOptions.UserName;
                                dbPPDetalle.AuditFechaModificacion = DateTime.Now;
                            }

                            dbPPDetalle.ProductoId = update.ProductoId;

                            DbPresupuestoProduccionDetalleCantidade cantSemanal = dbPPDetalle.PresupuestoProduccionDetalleCantidades.Where(c => c.NumeroSemana == update.NumeroSemana).SingleOrDefault();
                            if (cantSemanal == null)
                            {
                                cantSemanal = new DbPresupuestoProduccionDetalleCantidade();
                                cantSemanal.NumeroSemana = update.NumeroSemana;
                                cantSemanal.AuditUserNameCreacion = serviceOptions.UserName;
                                cantSemanal.AuditFechaCreacion = DateTime.Now;
                                dbPPDetalle.PresupuestoProduccionDetalleCantidades.Add(cantSemanal);
                            }
                            else
                            {
                                cantSemanal.AuditUserNameModificacion = serviceOptions.UserName;
                                cantSemanal.AuditFechaModificacion = DateTime.Now;
                            }

                            cantSemanal.Cantidad = update.Cantidad;
                        }

                        foreach (int idProdToRemove in deletedProductItems)
                        {
                            var detalleToRemove = dbPresupuestoProduccion.PresupuestoProduccionDetalles.Where(d => d.Id == idProdToRemove).SingleOrDefault();
                            if (detalleToRemove != null)
                            {
                                DbPresupuestoProduccionDetalleCantidade dbDetalleCantidad;
                                while (detalleToRemove.PresupuestoProduccionDetalleCantidades.Count > 0)
                                {
                                    dbDetalleCantidad = detalleToRemove.PresupuestoProduccionDetalleCantidades.First();
                                    ctx.DbPresupuestoProduccionDetalleCantidades.DeleteObject(dbDetalleCantidad);
                                }

                                detalleToRemove.PresupuestoProduccionDetalleCantidades.Clear();

                                ctx.DbPresupuestoProduccionDetalles.DeleteObject(detalleToRemove);
                            }
                        }

                        ctx.SaveChanges();
                    }
                }

                tScope.Complete();
            }
        }

        #region Plan Prod
        public List<PlanProduccion> GetPlanProduccion(ServiceOptions serviceOptions, int? planProduccionId, int? temporadaId, int? destinoId, int? proveedorId, DateTime? fechaDesde, DateTime? fechaHasta)
        {
            using (PlanProdEntities ctx = new PlanProdEntities())
            {
                IQueryable<DbPlanProduccion> query = ctx.DbPlanProduccions;

                if (planProduccionId.HasValue)
                {
                    query = query.Where(p => p.Id == planProduccionId.Value);
                }
                else
                {
                    if (temporadaId.HasValue)
                    {
                        query = query.Where(p => p.TemporadaId == temporadaId.Value);
                    }
                    if (destinoId.HasValue)
                    {
                        query = query.Where(p => p.DestinoId == destinoId.Value);
                    }

                    if (proveedorId.HasValue)
                    {
                        query = query.Where(p => p.ProveedorId == proveedorId.Value);
                    }

                    if (fechaDesde.HasValue)
                    {
                        query = query.Where(p => p.AuditFechaModificacion.Value >= fechaDesde.Value);
                    }
                    if (fechaHasta.HasValue)
                    {
                        query = query.Where(p => p.AuditFechaModificacion.Value <= fechaHasta.Value);
                    }
                }

                if (serviceOptions != null && serviceOptions.UsePages)
                {
                    query = query.Skip(serviceOptions.GetSkip()).Take(serviceOptions.PageSize);
                }

                List<PlanProduccion> planesDeProduccion = new List<PlanProduccion>();
                planesDeProduccion.AddRange((from p in query
                                             select new PlanProduccion()
                                                {
                                                    Id = p.Id,
                                                    DestinoId = p.DestinoId,
                                                    DestinoNombre = p.Destino.Nombre,
                                                    TemporadaId = p.TemporadaId,
                                                    TemporadaNombre = p.Temporada.Nombre,
                                                    EmpresaId = p.EmpresaId,
                                                    ProveedorId = p.ProveedorId,
                                                    ProveedorNombre = p.Proveedor.RazonSocial,
                                                    AuditFechaCreacion = p.AuditFechaCreacion,
                                                    AuditFechaModificacion = p.AuditFechaModificacion,
                                                    AuditUserNameCreacion = p.AuditUserNameCreacion,
                                                    AuditUserNameModificacion = p.AuditUserNameModificacion
                                                }).ToList());
                return planesDeProduccion;
            }
        }

        public void EliminarPlanProduccion(ServiceOptions serviceOptions, int planProduccionId)
        {
            using (TransactionScope tScope = new TransactionScope())
            {
                using (PlanProdEntities ctx = new PlanProdEntities())
                {
                    var planProduccion = ctx.DbPlanProduccions.Where(d => d.Id == planProduccionId).Single();
                    ctx.DbPlanProduccions.DeleteObject(planProduccion);
                    ctx.SaveChanges();
                }

                tScope.Complete();
            }
        }

        public void UpdatePlanProduccionDetalle(ServiceOptions serviceOptions, List<PlanProduccionDetalleProd> planProduccionDetalles)
        {
            using (TransactionScope tScope = new TransactionScope())
            {
                using (PlanProdEntities ctx = new PlanProdEntities())
                {
                    if (planProduccionDetalles.Count() > 0)
                    {
                        int planProduccionId = planProduccionDetalles.First().PlanProduccionId;
                        DbPlanProduccion dbPlanProduccion = ctx.DbPlanProduccions.Where(p => p.Id == planProduccionId).Single();

                        SaveDetallePlanProduccion(serviceOptions, ctx, dbPlanProduccion, planProduccionDetalles);

                        ctx.SaveChanges();
                    }
                }

                tScope.Complete();
            }
        }

        private void SaveDetallePlanProduccion(ServiceOptions serviceOptions, PlanProdEntities ctx, DbPlanProduccion dbPlanProduccion, List<PlanProduccionDetalleProd> planProduccionDetalles)
        {
            // Elimino todo lo que habia
            DbPlanProduccionDetalle dbDetalle;
            while (dbPlanProduccion.PlanProduccionDetalles.Count > 0)
            {
                dbDetalle = dbPlanProduccion.PlanProduccionDetalles.First();
                DbPlanProduccionDetalleCantidade dbDetalleCantidad;
                while (dbDetalle.PlanProduccionDetalleCantidades.Count > 0)
                {
                    dbDetalleCantidad = dbDetalle.PlanProduccionDetalleCantidades.First();
                    ctx.DbPlanProduccionDetalleCantidades.DeleteObject(dbDetalleCantidad);
                }

                dbDetalle.PlanProduccionDetalleCantidades.Clear();

                ctx.DbPlanProduccionDetalles.DeleteObject(dbDetalle);
            }

            dbPlanProduccion.PlanProduccionDetalles.Clear();

            ctx.SaveChanges();

            // Cargo lo nuevo
            foreach (PlanProduccionDetalleProd ppProd in planProduccionDetalles)
            {
                DbPlanProduccionDetalle dbPPDetalle = new DbPlanProduccionDetalle();
                dbPPDetalle.AuditFechaCreacion = DateTime.Now;
                dbPPDetalle.AuditUserNameCreacion = serviceOptions.UserName;
                // Encontrar el producto

                dbPPDetalle.ProductoId = GetProductoIdFromProductoColor(ctx, ppProd.ProductoIdExterno, ppProd.ColorIdExterno);
                //ctx.DbPlanProduccionDetalles.AddObject(dbPPDetalle);

                dbPlanProduccion.PlanProduccionDetalles.Add(dbPPDetalle);

                foreach (PlanProduccionDetalleProdSemanal ppProdCantSemana in ppProd.CantidadesPorSemana)
                {
                    DbPlanProduccionDetalleCantidade dbCantidadPorSemana = new DbPlanProduccionDetalleCantidade();
                    dbCantidadPorSemana.NumeroSemana = ppProdCantSemana.NumeroSemana;
                    dbCantidadPorSemana.Cantidad = ppProdCantSemana.Cantidad;
                    dbCantidadPorSemana.AuditFechaCreacion = DateTime.Now;
                    dbCantidadPorSemana.AuditUserNameCreacion = serviceOptions.UserName;
                    dbPPDetalle.PlanProduccionDetalleCantidades.Add(dbCantidadPorSemana);
                }
            }
        }

        public PlanProduccion SavePlanProduccion(ServiceOptions serviceOptions, PlanProduccion planProduccion)
        {

            using (TransactionScope tScope = new TransactionScope())
            {
                using (PlanProdEntities ctx = new PlanProdEntities())
                {
                    DbPlanProduccion dbPlanProduccion = ctx.DbPlanProduccions.Where(p => p.Id == planProduccion.Id).SingleOrDefault();

                    if (dbPlanProduccion == null)
                    {
                        dbPlanProduccion = new DbPlanProduccion();
                        dbPlanProduccion.AuditUserNameCreacion = serviceOptions.UserName;
                        dbPlanProduccion.AuditFechaCreacion = DateTime.Now;
                        ctx.DbPlanProduccions.AddObject(dbPlanProduccion);
                    }
                    else
                    {
                        dbPlanProduccion.AuditUserNameModificacion = serviceOptions.UserName;
                        dbPlanProduccion.AuditFechaModificacion = DateTime.Now;
                    }

                    dbPlanProduccion.TemporadaId = planProduccion.TemporadaId;
                    dbPlanProduccion.ProveedorId = planProduccion.ProveedorId;
                    dbPlanProduccion.DestinoId = planProduccion.DestinoId;
                    dbPlanProduccion.EmpresaId = planProduccion.EmpresaId;

                    ctx.SaveChanges();

                    SaveDetallePlanProduccion(serviceOptions, ctx, dbPlanProduccion, planProduccion.DetalleDeProductos);

                    ctx.SaveChanges();

                    planProduccion.Id = dbPlanProduccion.Id;

                }

                tScope.Complete();
                return planProduccion;
            }
        }

        public PlanProduccion GetPlanProduccionById(ServiceOptions serviceOptions, int id, List<int> detalleIds)
        {
            if (detalleIds == null)
            {
                detalleIds = new List<int>();
            }

            using (PlanProdEntities ctx = new PlanProdEntities())
            {
                DbPlanProduccion dbPlanProduccion = ctx.DbPlanProduccions.Where(p => p.Id == id).Single();

                PlanProduccion planProduccion = new PlanProduccion()
                {
                    Id = dbPlanProduccion.Id,
                    DestinoId = dbPlanProduccion.DestinoId,
                    DestinoNombre = dbPlanProduccion.Destino.Nombre,
                    TemporadaId = dbPlanProduccion.TemporadaId,
                    TemporadaNombre = dbPlanProduccion.Temporada.Nombre,
                    EmpresaId = dbPlanProduccion.EmpresaId,
                    ProveedorId = dbPlanProduccion.ProveedorId,
                    ProveedorNombre = dbPlanProduccion.Proveedor.RazonSocial,
                    PresupuestoProduccionId = dbPlanProduccion.PresupuestoProduccions.First().Id,
                    AuditFechaCreacion = dbPlanProduccion.AuditFechaCreacion,
                    AuditFechaModificacion = dbPlanProduccion.AuditFechaModificacion,
                    AuditUserNameCreacion = dbPlanProduccion.AuditUserNameCreacion,
                    AuditUserNameModificacion = dbPlanProduccion.AuditUserNameModificacion
                };

                int total = 0;
                PlanProduccionDetalleProd ppDetalle;
                bool loadDetalle = true;
                foreach (var dbPPDetalle in dbPlanProduccion.PlanProduccionDetalles)
                {
                    if (detalleIds.Count > 0)
                    {
                        loadDetalle = detalleIds.Where(di => di == dbPPDetalle.Id).Count() > 0;
                    }
                    else
                    {
                        loadDetalle = true;
                    }                    

                    if (loadDetalle)
                    {
                        ppDetalle = new PlanProduccionDetalleProd()
                        {
                            Id = dbPPDetalle.Id,
                            PlanProduccionId = dbPPDetalle.PlanProduccionId,
                            ProductoId = dbPPDetalle.ProductoId,
                            ProductoIdExterno = dbPPDetalle.Producto.ProductoIdExterno,
                            ProductoNombre = dbPPDetalle.Producto.ProductoNombre,
                            CalidadId = dbPPDetalle.Producto.CalidadId,
                            Calidad = GetCalidadDesc(dbPPDetalle.Producto.CalidadId),
                            ColorIdExterno = dbPPDetalle.Producto.ColorIdExterno,
                            ColorNombreExterno = dbPPDetalle.Producto.ColorNombreExterno
                        };

                        string estadoDesvioStr = Convert.ToString((char)EstadoCharAprobado.EsperandoAprobacion);
                        ppDetalle.DesvioPendienteDeAprobacion = dbPPDetalle.PlanProdDesvios.Where(d => d.Estado == estadoDesvioStr).Count() > 0;

                        planProduccion.DetalleDeProductos.Add(ppDetalle);

                        PlanProduccionDetalleProdSemanal ppDetalleCantidad;
                        foreach (var dbPPSemanaCantidad in dbPPDetalle.PlanProduccionDetalleCantidades)
                        {
                            ppDetalleCantidad = new PlanProduccionDetalleProdSemanal()
                            {
                                Id = dbPPSemanaCantidad.Id,
                                NumeroSemana = dbPPSemanaCantidad.NumeroSemana,
                                Cantidad = dbPPSemanaCantidad.Cantidad,
                                AuditFechaCreacion = dbPPSemanaCantidad.AuditFechaCreacion,
                                AuditFechaModificacion = dbPPSemanaCantidad.AuditFechaModificacion,
                                AuditUserNameCreacion = dbPPSemanaCantidad.AuditUserNameCreacion,
                                AuditUserNameModificacion = dbPPSemanaCantidad.AuditUserNameModificacion
                            };

                            total += dbPPSemanaCantidad.Cantidad;

                            ppDetalle.CantidadesPorSemana.Add(ppDetalleCantidad);
                        }
                    }
                }

                planProduccion.TotalPlan = total;

                return planProduccion;
            }
        }

        public List<Producto> GetProductosForPlan(ServiceOptions serviceOptions, string filter, int planProduccionId)
        {
            List<Producto> productoList = new List<Producto>();
            using (PlanProdEntities ctx = new PlanProdEntities())
            {
                IQueryable<DbProducto> query = ctx.DbProductoes;

                if (!String.IsNullOrEmpty(filter))
                {
                    query = query.Where(p => p.ProductoNombre.Contains(filter));
                }

                if (serviceOptions != null && serviceOptions.UsePages)
                {
                    query = query.Skip(serviceOptions.GetSkip()).Take(serviceOptions.PageSize);
                }

                productoList = (from p in query
                                where (from detalle in ctx.DbPlanProduccionDetalles
                                       where detalle.PlanProduccionId == planProduccionId
                                       select detalle.ProductoId).Contains(p.Id)
                                select new Producto()
                                {
                                    Id = p.Id,
                                    ProductoIdExterno = p.ProductoIdExterno,
                                    ProductoNombre = p.ProductoNombre,
                                    CalidadId = p.CalidadId,
                                    ColorIdExterno = p.ColorIdExterno,
                                    ColorNombreExterno = p.ColorNombreExterno,
                                    TemporadaLetra = p.TemporadaLetra
                                }).ToList();

                productoList.ForEach(p => p.Calidad = GetCalidadDesc(p.CalidadId));
            }

            return productoList;
        }

        public List<Producto> SearchProducts(ServiceOptions serviceOptions, SearchProductCriteria criteria)
        {
            List<Producto> productoList = new List<Producto>();
            using (PlanProdEntities ctx = new PlanProdEntities())
            {
                IQueryable<DbProducto> query = ctx.DbProductoes;

                if (!String.IsNullOrEmpty(criteria.CodigoProducto))
                {
                    query = query.Where(p => p.ProductoIdExterno.StartsWith(criteria.CodigoProducto));
                }

                if (!String.IsNullOrEmpty(criteria.CodigoColor))
                {
                    query = query.Where(p => p.ColorIdExterno == criteria.CodigoColor);
                }

                if (criteria.TemporadaId.HasValue)
                {
                    query = query.Where(p => p.TemporadaId == criteria.TemporadaId.Value);
                }

                if (criteria.CalidadId.HasValue)
                {
                    query = query.Where(p => p.CalidadId == criteria.CalidadId.Value);
                }                

                if (serviceOptions != null && serviceOptions.UsePages)
                {
                    query = query.Skip(serviceOptions.GetSkip()).Take(serviceOptions.PageSize);
                }

                productoList = (from p in query                               
                                select new Producto()
                                {
                                    Id = p.Id,
                                    ProductoIdExterno = p.ProductoIdExterno,
                                    ProductoNombre = p.ProductoNombre,
                                    CalidadId = p.CalidadId,
                                    ColorIdExterno = p.ColorIdExterno,
                                    ColorNombreExterno = p.ColorNombreExterno,
                                    TemporadaLetra = p.TemporadaLetra,
                                    TemporadaNombre = p.Temporada.Nombre,
                                    MarcaNombreExterno = p.MarcaNombreExerno,
                                    OrigenNombreExterno = p.OrigenNombreExterno,
                                    LineaNombreExterno = p.LineaNombreExterno,
                                    FamiliaNombreExterno = p.FamiliaNombreExterno,
                                    SubFamiliaNombreExterno = p.SubFamiliaNombreExterno
                                }).ToList();

                productoList.ForEach(p => p.Calidad = GetCalidadDesc(p.CalidadId));
            }

            return productoList;
        }

        public List<Producto> GetPossibleProductosForPlan(ServiceOptions serviceOptions, string filter, int planProduccionId)
        {
            List<Producto> productoList = new List<Producto>();
            using (PlanProdEntities ctx = new PlanProdEntities())
            {
                IQueryable<DbProducto> query = ctx.DbProductoes;

                if (!String.IsNullOrEmpty(filter))
                {
                    query = query.Where(p => p.ProductoNombre.Contains(filter));
                }

                if (serviceOptions != null && serviceOptions.UsePages)
                {
                    query = query.Skip(serviceOptions.GetSkip()).Take(serviceOptions.PageSize);
                }

                productoList = (from p in query
                                where !(from detalle in ctx.DbPlanProduccionDetalles
                                        where detalle.PlanProduccionId == planProduccionId
                                        select detalle.ProductoId).Contains(p.Id)
                                select new Producto()
                                {
                                    Id = p.Id,
                                    ProductoIdExterno = p.ProductoIdExterno,
                                    ProductoNombre = p.ProductoNombre,
                                    CalidadId = p.CalidadId,
                                    ColorIdExterno = p.ColorIdExterno,
                                    ColorNombreExterno = p.ColorNombreExterno,
                                    TemporadaLetra = p.TemporadaLetra
                                }).ToList();

                productoList.ForEach(p => p.Calidad = GetCalidadDesc(p.CalidadId));
            }

            return productoList;
        }

        public void UpdatePlanProduccionItems(ServiceOptions serviceOptions, int planProduccionId, List<UpdateCantidadPlanProd> updates, List<int> deletedProductItems)
        {

            using (TransactionScope tScope = new TransactionScope())
            {
                using (PlanProdEntities ctx = new PlanProdEntities())
                {
                    DbPlanProduccion dbPlanProduccion = ctx.DbPlanProduccions.Where(p => p.Id == planProduccionId).Single();

                    foreach (UpdateCantidadPlanProd update in updates)
                    {
                        DbPlanProduccionDetalle dbPPDetalle;

                        dbPPDetalle = dbPlanProduccion.PlanProduccionDetalles.Where(d => d.ProductoId == update.ProductoId).SingleOrDefault();

                        if (dbPPDetalle == null)
                        {
                            dbPPDetalle = new DbPlanProduccionDetalle();
                            dbPPDetalle.AuditUserNameCreacion = serviceOptions.UserName;
                            dbPPDetalle.AuditFechaCreacion = DateTime.Now;
                            dbPlanProduccion.PlanProduccionDetalles.Add(dbPPDetalle);
                        }
                        else
                        {
                            dbPPDetalle = dbPlanProduccion.PlanProduccionDetalles.Where(p => p.Id == update.Id).Single();
                            dbPPDetalle.AuditUserNameModificacion = serviceOptions.UserName;
                            dbPPDetalle.AuditFechaModificacion = DateTime.Now;
                        }

                        dbPPDetalle.ProductoId = update.ProductoId;

                        DbPlanProduccionDetalleCantidade cantSemanal = dbPPDetalle.PlanProduccionDetalleCantidades.Where(c => c.NumeroSemana == update.NumeroSemana).SingleOrDefault();
                        if (cantSemanal == null)
                        {
                            cantSemanal = new DbPlanProduccionDetalleCantidade();
                            cantSemanal.NumeroSemana = update.NumeroSemana;
                            cantSemanal.AuditUserNameCreacion = serviceOptions.UserName;
                            cantSemanal.AuditFechaCreacion = DateTime.Now;
                            dbPPDetalle.PlanProduccionDetalleCantidades.Add(cantSemanal);
                        }
                        else
                        {
                            cantSemanal.AuditUserNameModificacion = serviceOptions.UserName;
                            cantSemanal.AuditFechaModificacion = DateTime.Now;
                        }

                        cantSemanal.Cantidad = update.Cantidad;
                    }

                    foreach (int idProdToRemove in deletedProductItems)
                    {
                        var detalleToRemove = dbPlanProduccion.PlanProduccionDetalles.Where(d => d.Id == idProdToRemove).SingleOrDefault();
                        if (detalleToRemove != null)
                        {
                            DbPlanProduccionDetalleCantidade dbDetalleCantidad;
                            while (detalleToRemove.PlanProduccionDetalleCantidades.Count > 0)
                            {
                                dbDetalleCantidad = detalleToRemove.PlanProduccionDetalleCantidades.First();
                                ctx.DbPlanProduccionDetalleCantidades.DeleteObject(dbDetalleCantidad);
                            }

                            detalleToRemove.PlanProduccionDetalleCantidades.Clear();

                            ctx.DbPlanProduccionDetalles.DeleteObject(detalleToRemove);
                        }
                    }

                    ctx.SaveChanges();
                }

                tScope.Complete();
            }
        }

        #endregion
    }
}
